package ldk.fakewifi;

import android.app.Activity;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;

import java.util.ArrayList;
import java.util.List;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

import static de.robv.android.xposed.XposedHelpers.*;

/**
 * Created by ke on 16-10-23.
 */
public class FakeWifi implements IXposedHookLoadPackage{
    @SuppressWarnings("unused")
    private static final String TAG = "FakeWifi";

    private static final String CLASS_CONNECTIVITY_MANAGER = ConnectivityManager.class.getName();
    private static final String CLASS_WIFI_MANAGER = WifiManager.class.getName();

    private XSharedPreferences mPreferences;

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        if(!NeedsToFakeWifi.getInstance().isNeedsToFakeWifi(loadPackageParam.packageName)) return;

        FakeWifiUtil.log(TAG, "handleLoadPackage, and packageName is %s", loadPackageParam.packageName);
        mPreferences = new XSharedPreferences(loadPackageParam.packageName);

        findAndHookMethod((Class) Activity.class, "onResume", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                mPreferences.reload();
            }
        });

        // ============================================  *
        // ConnectivityManager targets:                  *
        //    getActiveNetworkInfo();                  *
        // 用于欺骗是wifi连接
        // ============================================  *
        findAndHookMethod(CLASS_CONNECTIVITY_MANAGER, loadPackageParam.classLoader,
                "getActiveNetworkInfo", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                fakeAfterGetActiveNetworkInfo(param);
            }
        });

        findAndHookMethod(CLASS_WIFI_MANAGER, loadPackageParam.classLoader,
                "getConnectionInfo", new XC_MethodHook() {
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        fakeAfterGetConnectionInfo(param, loadPackageParam);
                    }
                });

        findAndHookMethod(CLASS_CONNECTIVITY_MANAGER, loadPackageParam.classLoader, "getAllNetworkInfo", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                fakeAfterGetAllNetworkInfo(param);
            }
        });

        findAndHookMethod(CLASS_CONNECTIVITY_MANAGER, loadPackageParam.classLoader, "getNetworkInfo", int.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                fakeAfterGetNetworkInfo(param, loadPackageParam);
            }
        });


        findAndHookMethod(CLASS_WIFI_MANAGER, loadPackageParam.classLoader, "getScanResults", new XC_MethodReplacement() {
            @Override
            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                return fakeAfterGetScanResults(param);
            }
        });
    }

    private List<ScanResult> fakeAfterGetScanResults(XC_MethodHook.MethodHookParam param) {
        FakeWifiUtil.log(TAG, "fakeAfterGetScanResults");
        List<ScanResult> resultList = new ArrayList<>();
        ScanResult scanResult = (ScanResult) newInstance(ScanResult.class);
        scanResult.BSSID = "e4:f4:c6:06:44:7d";
        scanResult.SSID = "XBLX2015";
        scanResult.level = -46;
        resultList.add(scanResult);
        return resultList;
    }

    private void fakeAfterGetNetworkInfo(XC_MethodHook.MethodHookParam param, XC_LoadPackage.LoadPackageParam loadPackageParam) {
        param.setResult(fakeWifiInfo(loadPackageParam));
        FakeWifiUtil.log(TAG, "fakeAfterGetNetworkInfo");
    }

    private void fakeAfterGetAllNetworkInfo(XC_MethodHook.MethodHookParam param) {
        param.setResult(new NetworkInfo[]{fakeNetworkInfo()});
        FakeWifiUtil.log(TAG, "fakeAfterGetAllNetworkInfo");
    }

    private void fakeAfterGetConnectionInfo(XC_MethodHook.MethodHookParam param, XC_LoadPackage.LoadPackageParam loadPackageParam) {
        FakeWifiUtil.log(TAG, "fakeAfterGetConnectionInfo, and packageName is %s.", loadPackageParam.packageName);
        param.setResult(fakeWifiInfo(loadPackageParam));
    }

    private WifiInfo fakeWifiInfo(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        WifiInfo wifiInfo = (WifiInfo) newInstance(WifiInfo.class);
        setIntField(wifiInfo, "mNetworkId", 1);
        setObjectField(wifiInfo, "mBSSID", "e4:f4:c6:06:44:7d");
        setObjectField(wifiInfo, "mMacAddress", "e4:f4:c6:06:44:7d");
        Class cls = findClass("android.net.wifi.WifiSsid", loadPackageParam.classLoader);
        Object wifiSsid = callStaticMethod(cls, "createFromAsciiEncoded", "XBLX2015");
        setObjectField(wifiInfo, "mWifiSsid", wifiSsid);
        return wifiInfo;
    }


    private void fakeAfterGetActiveNetworkInfo(XC_MethodHook.MethodHookParam param){
        param.setResult(fakeNetworkInfo());
        FakeWifiUtil.log(TAG, "fakeAfterGetActiveNetworkInfo");
    }

    private NetworkInfo fakeNetworkInfo() {
        NetworkInfo networkInfo = (NetworkInfo) newInstance(NetworkInfo.class,
                ConnectivityManager.TYPE_WIFI, 0, "WIFI", null);
        setObjectField(networkInfo, "mState", NetworkInfo.State.CONNECTED);
        setObjectField(networkInfo, "mDetailedState", NetworkInfo.DetailedState.CONNECTED);
        setBooleanField(networkInfo, "mIsAvailable", true);
        return networkInfo;
    }

}
